﻿//Copyright (c) 2009, Offbeat Guides
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are 
//permitted provided that the following conditions are met:
//
//* Redistributions of source code must retain the above copyright notice, this list of 
//conditions and the following disclaimer.
//
//* Redistributions in binary form must reproduce the above copyright notice, this list 
//of conditions and the following disclaimer in the documentation and/or other materials 
//provided with the distribution.
//
//* Neither the name of Offbeat Guides nor the names of its contributors may be used to 
//endorse or promote products derived from this software without specific prior written 
//permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
//EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
//SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
//TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections;

namespace Amazon.Services.S3
{
    public static class File
    {
        #region Static Methods
        public static FileStream Create(AmazonAccount account, String fullpath)
        {
            return File.Create(account, fullpath, 0x1000);
        }

        public static FileStream Create(AmazonAccount account, String fullpath, Int32 bufferSize)
        {
            if (account == null)
                throw new ArgumentNullException("account");
            if (String.IsNullOrEmpty(fullpath))
                throw new ArgumentNullException("fullpath");
            if (bufferSize < 0)
                throw new ArgumentOutOfRangeException("bufferSize");

            Match match = FileSystemHelper.PathRegex.Match(fullpath);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the directory specified. directory = \"{0}\".", fullpath));

            String bucket = match.Groups["bucket"].Value;
            if (bucket.Length > DriveInfo.MaxNameLength || bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            String path = match.Groups["path"].Value.Replace("\\", "/").Trim('/');
            if (path.Length > DirectoryInfo.MaxPathLength || path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            ServiceClient client = new ServiceClient(account, S3Configuration.Default);
            if (FileSystemHelper.FileExists(client, bucket, path))
                throw new IOException("Directory already exists and cannot be overwritten.");

            return new FileStream(client, bucket, path, FileMode.Create, FileAccess.Write, FileShare.None, false);
        }

        public static void Delete(AmazonAccount account, String fullpath)
        {
            if (account == null)
                throw new ArgumentNullException("account");
            if (String.IsNullOrEmpty(fullpath))
                throw new ArgumentNullException("fullpath");

            Match match = FileSystemHelper.PathRegex.Match(fullpath);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the directory specified. directory = \"{0}\".", fullpath));

            String bucket = match.Groups["bucket"].Value;
            if (bucket.Length > DriveInfo.MaxNameLength || bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            String path = match.Groups["path"].Value.Replace("\\", "/").Trim('/');
            if (path.Length > DirectoryInfo.MaxPathLength || path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            ServiceClient client = new ServiceClient(account, S3Configuration.Default);

            if (!FileSystemHelper.FileExists(client, bucket, path))
                throw new IOException("File does not exist.");

            FileSystemHelper.DeleteFile(client, bucket, path);
        }

        public static Boolean Exists(AmazonAccount account, String fullpath)
        {
            if (account == null)
                throw new ArgumentNullException("account");
            if (String.IsNullOrEmpty(fullpath))
                throw new ArgumentNullException("fullpath");

            Match match = FileSystemHelper.PathRegex.Match(fullpath);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the file specified. directory = \"{0}\".", fullpath));

            String bucket = match.Groups["bucket"].Value;
            if (bucket.Length > DriveInfo.MaxNameLength || bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            String path = match.Groups["path"].Value.Replace("\\", "/").Trim('/');
            if (path.Length > DirectoryInfo.MaxPathLength || path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            ServiceClient client = new ServiceClient(account, S3Configuration.Default);
            return FileSystemHelper.FileExists(client, bucket, path);
        }

        public static FileStream Open(AmazonAccount account, String fullpath, FileMode mode)
        {
            return File.Open(account, fullpath, mode, (mode == FileMode.Append) ? FileAccess.Write : FileAccess.ReadWrite, FileShare.None);
        }

        public static FileStream Open(AmazonAccount account, String fullpath, FileMode mode, FileAccess access)
        {
            return File.Open(account, fullpath, mode, access, FileShare.None);
        }

        public static FileStream Open(AmazonAccount account, String fullpath, FileMode mode, FileAccess access, FileShare share)
        {
            if (account == null)
                throw new ArgumentNullException("account");
            if (String.IsNullOrEmpty(fullpath))
                throw new ArgumentNullException("fullpath");

            Match match = FileSystemHelper.PathRegex.Match(fullpath);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the file specified. directory = \"{0}\".", fullpath));

            String bucket = match.Groups["bucket"].Value;
            if (bucket.Length > DriveInfo.MaxNameLength || bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            String path = match.Groups["path"].Value.Replace("\\", "/").Trim('/');
            if (path.Length > DirectoryInfo.MaxPathLength || path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            ServiceClient client = new ServiceClient(account, S3Configuration.Default);
            return new FileStream(client, bucket, path, mode, access, share, false);
        }

        public static FileStream OpenRead(AmazonAccount account, String fullpath)
        {
            return new FileStream(account, fullpath, FileMode.Open, FileAccess.Read, FileShare.Read);
        }

        public static StreamReader OpenText(AmazonAccount account, String fullpath)
        {
            return new StreamReader(new FileStream(account, fullpath, FileMode.Open, FileAccess.Read, FileShare.Read));
        }

        public static FileStream OpenWrite(AmazonAccount account, String fullpath)
        {
            return new FileStream(account, fullpath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
        }

        public static Byte[] ReadAllBytes(AmazonAccount account, String fullpath)
        {
            byte[] buffer;
            using (FileStream stream = new FileStream(account, fullpath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                int offset = 0;
                if (stream.Length > 0x7fffffffL)
                    throw new IOException();

                int count = 0;
                buffer = new byte[stream.Length];
                do
                {
                    if ((count = stream.Read(buffer, offset, (int)stream.Length)) == 0)
                        return buffer;

                    offset += count;
                } while (count > 0);
            }
            return buffer;
        }

        public static String[] ReadAllLines(AmazonAccount account, String fullpath)
        {
            return File.ReadAllLines(account, fullpath, Encoding.UTF8);
        }

        public static String[] ReadAllLines(AmazonAccount account, String fullpath, Encoding encoding)
        {
            List<String> list = new List<String>();
            using (StreamReader reader = new StreamReader(new FileStream(account, fullpath, FileMode.Open, FileAccess.Read, FileShare.Read), encoding))
            {
                string str;
                while ((str = reader.ReadLine()) != null)
                    list.Add(str);
            }
            return list.ToArray();
        }

        public static String ReadAllText(AmazonAccount account, String fullpath)
        {
            return File.ReadAllText(account, fullpath, FileSystemHelper.Encoding);
        }

        public static String ReadAllText(AmazonAccount account, String fullpath, Encoding encoding)
        {
            using (StreamReader reader = new StreamReader(new FileStream(account, fullpath, FileMode.Open, FileAccess.Read, FileShare.Read), encoding))
                return reader.ReadToEnd();
        }

        public static void WriteAllBytes(AmazonAccount account, String fullpath, Byte[] bytes)
        {
            if (bytes == null)
                throw new ArgumentNullException("bytes");

            using (FileStream stream = new FileStream(account, fullpath, FileMode.Create, FileAccess.Write, FileShare.Read))
            {
                stream.Write(bytes, 0, bytes.Length);
                stream.Flush();
            }
        }

        public static void WriteAllLines(AmazonAccount account, String fullpath, String[] contents)
        {
            File.WriteAllLines(account, fullpath, contents, FileSystemHelper.Encoding);
        }

        public static void WriteAllLines(AmazonAccount account, String fullpath, String[] contents, Encoding encoding)
        {
            if (contents == null)
                throw new ArgumentNullException("contents");

            using (StreamWriter writer = new StreamWriter(new FileStream(account, fullpath, FileMode.Create, FileAccess.Write, FileShare.Read), encoding))
            {
                foreach (String str in contents)
                    writer.WriteLine(str);
            }
        }

        public static void WriteAllText(AmazonAccount account, String fullpath, String contents)
        {
            File.WriteAllText(account, fullpath, contents, FileSystemHelper.Encoding);
        }

        public static void WriteAllText(AmazonAccount account, String fullpath, String contents, Encoding encoding)
        {
            using (StreamWriter writer = new StreamWriter(new FileStream(account, fullpath, FileMode.Create, FileAccess.Write, FileShare.Read), encoding))
                writer.Write(contents);
        }

        public static void SetAccessControl(AmazonAccount account, String fullpath, AccessControlModes level)
        {
            if (account == null)
                throw new ArgumentNullException("account");
            if (String.IsNullOrEmpty(fullpath))
                throw new ArgumentNullException("fullpath");

            Match match = FileSystemHelper.PathRegex.Match(fullpath);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the file specified. directory = \"{0}\".", fullpath));

            String bucket = match.Groups["bucket"].Value;
            if (bucket.Length > DriveInfo.MaxNameLength || bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            String path = match.Groups["path"].Value.Replace("\\", "/").Trim('/');
            if (path.Length > DirectoryInfo.MaxPathLength || path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            FileSystemHelper.SetAccessControl(new ServiceClient(account, S3Configuration.Default), bucket, path, level);
        }

        public static AccessControlModes GetAccessControl(AmazonAccount account, String fullpath)
        {
            if (account == null)
                throw new ArgumentNullException("account");
            if (String.IsNullOrEmpty(fullpath))
                throw new ArgumentNullException("fullpath");

            Match match = FileSystemHelper.PathRegex.Match(fullpath);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the file specified. directory = \"{0}\".", fullpath));

            String bucket = match.Groups["bucket"].Value;
            if (bucket.Length > DriveInfo.MaxNameLength || bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            String path = match.Groups["path"].Value.Replace("\\", "/").Trim('/');
            if (path.Length > DirectoryInfo.MaxPathLength || path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            return FileSystemHelper.GetAccessControl(new ServiceClient(account, S3Configuration.Default), bucket, path);
        }
        #endregion
    }

    public enum AccessControlModes : int
    {
        Private,
        Public
    }
}
